package org.jsola.hr.service.impl;

import com.alibaba.excel.EasyExcel;
import org.jsola.common.BeanKit;
import org.jsola.core.Page;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.constant.SocCheckWrongsStatus;
import org.jsola.hr.constant.SocPayCostType;
import org.jsola.hr.dao.ISocCheckDAO;
import org.jsola.hr.dao.ISocCheckItemDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.query.SocCheckCustomQuery;
import org.jsola.hr.query.SocCheckQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.service.excel.SocCheckImportListener;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.EmpSocStatus.INCREMENT;
import static org.jsola.hr.constant.EmpSocStatus.NORMAL_SOC;
import static org.jsola.hr.constant.HrConstants.*;

/**
 * 社保校验表
 *
 * @author zhr
 */
@Service("hrSocCheckServiceImpl")
public class SocCheckServiceImpl implements ISocCheckService {

    @Autowired
    private ISocCheckDAO socCheckDAO;

    @Autowired
    private ISocCheckItemService socCheckItemService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private ISocCheckItemDAO socCheckItemDAO;

    @Autowired
    private IEmpSocItemService empSocItemService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SocCheckVO save(SocCheckAddDTO socCheckAddDTO, TokenUser tokenUser) {
        // 转DO
        SocCheckDO socCheckDO = socCheckAddDTO.to(SocCheckDO.class);
        // 保存
        socCheckDO = save(socCheckDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return socCheckDO.to(SocCheckVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(SocCheckUpdateDTO socCheckUpdateDTO, TokenUser tokenUser) {
        //转DO
        SocCheckDO socCheckDO = socCheckUpdateDTO.to(SocCheckDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(socCheckDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... socCheckIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) socCheckIds);
    }


    @Override
    public SocCheckVO selectById(String socCheckId, String siteId) {
        SocCheckDO socCheckDO = selectDOById(socCheckId, siteId);
        if (socCheckDO == null) {
            return null;
        }
        return socCheckDO.to(SocCheckVO.class);
    }

    @Override
    public List<SocCheckListVO> select(SocCheckQuery socCheckQuery, String siteId) {
        List<SocCheckDO> socCheckDOList = selectDO(socCheckQuery, siteId);
        if (CollectionUtils.isEmpty(socCheckDOList)) {
            return socCheckDOList == null ? null : new ArrayList<>();
        }
        return socCheckDOList.stream()
                .map(socCheckDO -> socCheckDO.to(SocCheckListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(SocCheckQuery socCheckQuery, String siteId) {
        Example example = buildExample(socCheckQuery, siteId);
        return socCheckDAO.selectCountByExample(example);
    }

    @Override
    public Page<SocCheckListVO> selectPage(SocCheckQuery socCheckQuery, String siteId) {
        Example example = buildExample(socCheckQuery, siteId);
        Page<SocCheckDO> page = socCheckDAO.selectPageByExample(example,
                socCheckQuery.getPageNo(),
                socCheckQuery.getPageSize());

        return page.to(SocCheckListVO.class);
    }

    @Override
    public SocCheckDO selectDOById(String socCheckId, String siteId) {
        return listById(socCheckId, siteId);
    }

    @Override
    public List<SocCheckDO> selectDO(SocCheckQuery socCheckQuery, String siteId) {
        Example example = buildExample(socCheckQuery, siteId);
        return socCheckDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void deleteByMonthIdList(List<String> socMonthIdList, TokenUser tokenUser) {
        Example example = new Example(SocCheckDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andIn("socMonthId", socMonthIdList);
        socCheckDAO.deleteByExample(example);
    }

    @Override
    public List<SocCheckDO> selectByMonthId(String socMonthId, TokenUser tokenUser) {
        Example example = new Example(SocCheckDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("socMonthId", socMonthId);
        example.orderBy("gmtCreate").desc();
        return socCheckDAO.selectByExample(example);
    }


    @Override
    public Page<SocCheckVO> pageByMonthIdAndCheckType(SocCheckCustomQuery socCheckCustomQuery, TokenUser tokenUser) {
        Example example = new Example(SocCheckDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("checkType", socCheckCustomQuery.getCheckType())
                .andEqualTo("socMonthId", socCheckCustomQuery.getMonthId());
        example.orderBy("gmtCreate").desc();
        Page<SocCheckDO> socCheckPage =
                socCheckDAO.selectPageByExample(example, socCheckCustomQuery.getPageNo(), socCheckCustomQuery.getPageSize());
        Page<SocCheckVO> resultPage = socCheckPage.to(SocCheckVO.class);
        List<SocCheckVO> pageItem = resultPage.getEntities();
        for (SocCheckVO socCheckVO : pageItem) {
            List<SocCheckItemVO> socCheckItemList = socCheckItemService.additionalSocCheckItem(socCheckVO.getId(), null, tokenUser);
            socCheckVO.setSocCheckItemList(socCheckItemList);
        }
        resultPage.setEntities(pageItem);
        return resultPage;
    }


    @Override
    public Integer selectCountByMonthId(Integer checkType, String socMonthId, TokenUser tokenUser) {
        Example example = new Example(SocCheckDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("checkType", checkType)
                .andEqualTo("socMonthId", socMonthId);
        return socCheckDAO.selectCountByExample(example);
    }

    @Override
    public List<SocCheckParseImportListVO> parseSocCheckImportFile(File file, TokenUser tokenUser) {
        SocCheckImportListener socCheckImportListener = new SocCheckImportListener();
        socCheckImportListener.setTokenUser(tokenUser);
        socCheckImportListener.setParseOnly(true);
        // 读取excel 只解析不入库
        EasyExcel.read(file, SocCheckItemImportDTO.class, socCheckImportListener).sheet().doRead();
        return socCheckImportListener.getParseList();
    }

    @Override
    public List<SocCheckParseImportListVO> saveSocCheckImportFile(File file, SocCheckAddDTO socCheckAddDTO, TokenUser tokenUser) {
        SocCheckImportListener socCheckImportListener = new SocCheckImportListener();
        socCheckImportListener.setTokenUser(tokenUser);
        socCheckImportListener.setSocCheckAddDTO(socCheckAddDTO);
        socCheckImportListener.setParseOnly(false);
        EasyExcel.read(file, SocCheckItemImportDTO.class, socCheckImportListener).sheet().doRead();
        return socCheckImportListener.getParseList();
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void checkWebSiteDate(SocCheckCustomQuery socCheckCustomQuery, TokenUser tokenUser) {
        SocCheckAddDTO saveAddDTO = socCheckCustomQuery.to(SocCheckAddDTO.class);
        saveAddDTO.setSocMonthId(socCheckCustomQuery.getMonthId());
        List<String> empSocInfoIdList = new ArrayList<>();
        // 初始化一次检验结果,查询到系统中这些参保人员
        SocCheckDO socCheckDO = saveInitCheck(saveAddDTO, empSocInfoIdList, false, tokenUser);
        ISyncSocInterface syncSocInterface =
                (ISyncSocInterface) BeanKit.getBean(socCheckCustomQuery.getSyncServiceBeanName());
        Map<String, List<SyncSocUniversalDTO>> resultMap =
                syncSocInterface.checkInfo(socCheckCustomQuery, tokenUser);
        List<SyncSocUniversalDTO> officialSocCheckList = new ArrayList<>();
        List<SyncSocUniversalDTO> officialPfCheckList = new ArrayList<>();
        List<SyncSocUniversalDTO> officialMeCheckList = new ArrayList<>();
        List<OfficialPackageDTO> socPackageList = new ArrayList<>();
        List<OfficialPackageDTO> pfPackageList = new ArrayList<>();
        List<OfficialPackageDTO> mePackageList = new ArrayList<>();
        // 分别判断社保医保公积金是否校验,拿到对应社保局医保局公积金网站的数据。
        checkWebSite(socCheckCustomQuery, resultMap, officialSocCheckList, officialPfCheckList, officialMeCheckList);
        // 把官方数据封装一下,计算每个人的金额
        if (!CollectionUtils.isEmpty(officialSocCheckList)) {
            packageOfficialDate(officialSocCheckList, socPackageList);
        }
        if (!CollectionUtils.isEmpty(officialPfCheckList)) {
            packageOfficialDate(officialPfCheckList, pfPackageList);
        }
        if (!CollectionUtils.isEmpty(officialMeCheckList)) {
            packageOfficialDate(officialMeCheckList, mePackageList);
        }
        // 把两个List也合成一个
        List<SocCheckItemImportDTO> synthesisList =
                synthesisSocPf(socCheckCustomQuery.getYear(), socCheckCustomQuery.getMonth(), socPackageList, pfPackageList);
        // 入库
        saveOfficialWrongsDate(socCheckCustomQuery, empSocInfoIdList, synthesisList,
                socCheckDO, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void saveExcelDate(List<SocCheckItemImportDTO> socCheckItemImportList,
                              SocCheckAddDTO socCheckAddDTO, TokenUser tokenUser) {
        List<String> empSocInfoIdList = new ArrayList<>();
        SocCheckDO socCheckDO = saveInitCheck(socCheckAddDTO, empSocInfoIdList, true, tokenUser);
        List<SocCheckItemDO> socCheckItemList = new ArrayList<>();
        for (SocCheckItemImportDTO socCheckItemImportDTO : socCheckItemImportList) {
            String year = socCheckItemImportDTO.getBelongsDate().substring(0, 4);
            String month = socCheckItemImportDTO.getBelongsDate().substring(4);
            SocCheckItemDO socCheckItemDO = new SocCheckItemDO();
            socCheckItemDO.setSocCheckId(socCheckDO.getId());
            socCheckItemDO.setYear(year);
            socCheckItemDO.setPayCostType(Objects.requireNonNull(SocPayCostType.findByDesc(socCheckItemImportDTO.getPayCostType())).getValue());
            socCheckItemDO.setMonth(month);
            socCheckItemDO.setCompanyId(socCheckAddDTO.getCompanyId());
            socCheckItemDO.setEmpName(socCheckItemImportDTO.getName());
            addSystemColumn(socCheckItemDO, tokenUser);
            // 根据姓名身份证号查询系统中是否有此人
            EmpInfoDO empInfoDO = empInfoService.selectEmpByNameAndIdCard(socCheckItemImportDTO.getName(),
                    socCheckItemImportDTO.getIdCard(), socCheckAddDTO.getCompanyId(), tokenUser);
            // 系统内无excel中的这个人
            if (ObjectUtils.isEmpty(empInfoDO)) {
                // 封装一个子项
                encapsulationCheckItem(socCheckItemImportDTO, null, socCheckItemDO);
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SYS_NO_PERSON.getValue());
                socCheckItemList.add(socCheckItemDO);
                socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + 1);
                continue;
            }
            socCheckItemDO.setEmpId(empInfoDO.getId());
            socCheckItemDO.setIdCard(empInfoDO.getIdCard());
            EmpSocInfoDO empSocInfoDO = empSocInfoService.selectEmpSocInfoByYearMonthEmpIdPayCostTypeMonthId(year, month,
                    Objects.requireNonNull(SocPayCostType.findByDesc(socCheckItemImportDTO.getPayCostType())).getValue(),
                    empInfoDO.getId(), socCheckAddDTO.getSocMonthId(), socCheckAddDTO.getCompanyId(), tokenUser.getSiteId());
            // 系统内有这个人但是excel中有数据但是系统中没数据
            if (ObjectUtils.isEmpty(empSocInfoDO)) {
                // 封装一个子项
                encapsulationCheckItem(socCheckItemImportDTO, null, socCheckItemDO);
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.NO_SOC.getValue());
                socCheckItemList.add(socCheckItemDO);
                socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + 1);
                continue;
            }
            encapsulationCheckItem(socCheckItemImportDTO, empSocInfoDO, socCheckItemDO);
            // 这边每匹配到一个  就删一个  最后剩下的就是  excel里面没有但是系统中有的
            empSocInfoIdList.remove(empSocInfoDO.getId());
            checkWrongs(socCheckAddDTO.getCheckType(), socCheckItemDO, socCheckItemList, socCheckDO);
        }
        if (!CollectionUtils.isEmpty(empSocInfoIdList)) {
            List<EmpSocInfoDO> excelNoExist = empSocInfoService.selectByIdList(empSocInfoIdList, tokenUser);
            List<String> empIdList = excelNoExist.stream().map(EmpSocInfoDO::getEmpId).collect(Collectors.toList());
            List<EmpInfoDO> empInfoList = empInfoService.selectDoByIds(empIdList);
            Map<String, EmpInfoDO> empInfoMap =
                    empInfoList.stream().collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
            int size = excelNoExist.size();
            for (EmpSocInfoDO empSocInfoDO : excelNoExist) {
                EmpInfoDO empInfoDO = empInfoMap.get(empSocInfoDO.getEmpId());
                if(ObjectUtils.isEmpty(empInfoDO)){
                    size--;
                    continue;
                }
                SocCheckItemDO socCheckItemDO = new SocCheckItemDO();
                BeanUtils.copyProperties(empSocInfoDO, socCheckItemDO);
                socCheckItemDO.setId(null);
                socCheckItemDO.setEmpName(empInfoDO.getName());
                socCheckItemDO.setIdCard(empInfoDO.getIdCard());
                socCheckItemDO.setSocCheckId(socCheckDO.getId());
                addSystemColumn(socCheckItemDO, tokenUser);
                encapsulationCheckItem(null, empSocInfoDO, socCheckItemDO);
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.EXCEL_NO_PERSON.getValue());
                socCheckItemList.add(socCheckItemDO);
            }
            socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + size);
        }
        if (!CollectionUtils.isEmpty(socCheckItemList)) {
            calculationDifference(socCheckItemList, socCheckDO, tokenUser);
            socCheckItemDAO.insertListAndSetId(socCheckItemList);
        }
    }

    @Override
    public BigDecimal checkNull(BigDecimal checkSource) {
        if (ObjectUtils.isEmpty(checkSource)) {
            checkSource = new BigDecimal("0");
        }
        return checkSource;
    }

    /**
     * 根据传入的socCheckItemImportDTO和empSocInfoDO把他们两个封装成一个错误子项
     */
    private void encapsulationCheckItem(SocCheckItemImportDTO socCheckItemImportDTO, EmpSocInfoDO empSocInfoDO,
                                        SocCheckItemDO socCheckItemDO) {
        BigDecimal zero = new BigDecimal("0");
        if (!ObjectUtils.isEmpty(socCheckItemImportDTO)) {
            socCheckItemDO.setExcelPersonalSocAmount(new BigDecimal(socCheckItemImportDTO.getPersonalSocAmount()));
            socCheckItemDO.setExcelPersonalPfAmount(new BigDecimal(socCheckItemImportDTO.getPersonalPfAmount()));
            socCheckItemDO.setExcelCompanySocAmount(new BigDecimal(socCheckItemImportDTO.getCompanySocAmount()));
            socCheckItemDO.setExcelCompanyPfAmount(new BigDecimal(socCheckItemImportDTO.getCompanyPfAmount()));
            socCheckItemDO.setExcelServiceCost(new BigDecimal(socCheckItemImportDTO.getServiceCost()));
            socCheckItemDO.setExcelSocialSecurityFine(new BigDecimal(socCheckItemImportDTO.getSocialSecurityFine()));
            socCheckItemDO.setExcelAccumulationFundFine(new BigDecimal(socCheckItemImportDTO.getAccumulationFundFine()));
            socCheckItemDO.setIdCard(socCheckItemImportDTO.getIdCard());
        } else {
            socCheckItemDO.setExcelPersonalSocAmount(zero);
            socCheckItemDO.setExcelPersonalPfAmount(zero);
            socCheckItemDO.setExcelCompanySocAmount(zero);
            socCheckItemDO.setExcelCompanyPfAmount(zero);
            socCheckItemDO.setExcelServiceCost(zero);
            socCheckItemDO.setExcelSocialSecurityFine(zero);
            socCheckItemDO.setExcelAccumulationFundFine(zero);
        }
        if (!ObjectUtils.isEmpty(empSocInfoDO)) {
            socCheckItemDO.setSysPersonalSocAmount(empSocInfoDO.getPersonalSocAmount());
            socCheckItemDO.setSysPersonalPfAmount(empSocInfoDO.getPersonalPfAmount());
            socCheckItemDO.setSysCompanySocAmount(empSocInfoDO.getCompanySocAmount());
            socCheckItemDO.setSysCompanyPfAmount(empSocInfoDO.getCompanyPfAmount());
            socCheckItemDO.setSysServiceCost(checkNull(empSocInfoDO.getServiceCost()));
            socCheckItemDO.setSysSocialSecurityFine(checkNull(empSocInfoDO.getSocialSecurityFine()));
            socCheckItemDO.setSysAccumulationFundFine(checkNull(empSocInfoDO.getAccumulationFundFine()));
            socCheckItemDO.setSocProgId(empSocInfoDO.getProgId());
        } else {
            socCheckItemDO.setSysPersonalSocAmount(zero);
            socCheckItemDO.setSysPersonalPfAmount(zero);
            socCheckItemDO.setSysCompanySocAmount(zero);
            socCheckItemDO.setSysCompanyPfAmount(zero);
            socCheckItemDO.setSysServiceCost(zero);
            socCheckItemDO.setSysSocialSecurityFine(zero);
            socCheckItemDO.setSysAccumulationFundFine(zero);
        }
        if (!ObjectUtils.isEmpty(socCheckItemImportDTO) && !ObjectUtils.isEmpty(empSocInfoDO)) {
            socCheckItemDO.setEmpSocInfoId(empSocInfoDO.getId());
        }
    }

    /**
     * 根据一个Excel对象和系统对象判断是否有差值,哪项有差值
     */
    private void checkWrongs(Integer checkType, SocCheckItemDO socCheckItemDO,
                             List<SocCheckItemDO> socCheckItemList, SocCheckDO socCheckDO) {
        boolean wrongs = false;
        if (1 == checkType) {
            if (socCheckItemDO.getSysServiceCost().subtract(socCheckItemDO.getExcelServiceCost()).
                    compareTo(BigDecimal.ZERO) != 0) {
                wrongs = true;
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SERVICE_COST_WRONGS.getValue());
            }
        }
        if (socCheckItemDO.getSysSocialSecurityFine().subtract(socCheckItemDO.getExcelSocialSecurityFine()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.LATE_FEES_WRONGS.getValue());
        }
        if (socCheckItemDO.getSysAccumulationFundFine().subtract(socCheckItemDO.getExcelAccumulationFundFine()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.LATE_FEES_WRONGS.getValue());
        }
        if (socCheckItemDO.getSysPersonalPfAmount().subtract(socCheckItemDO.getExcelPersonalPfAmount()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.PF_WRONGS.getValue());
        }
        if (socCheckItemDO.getSysCompanyPfAmount().subtract(socCheckItemDO.getExcelCompanyPfAmount()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.PF_WRONGS.getValue());
        }
        if (socCheckItemDO.getSysPersonalSocAmount().subtract(socCheckItemDO.getExcelPersonalSocAmount()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SOC_WRONGS.getValue());
        }
        if (socCheckItemDO.getSysCompanySocAmount().subtract(socCheckItemDO.getExcelCompanySocAmount()).
                compareTo(BigDecimal.ZERO) != 0) {
            wrongs = true;
            socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SOC_WRONGS.getValue());
        }
        if (wrongs) {
            socCheckItemList.add(socCheckItemDO);
            socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + 1);
        }
    }

    /**
     * 根据错误信息子项列表计算这些错误项的差值
     */
    private void calculationDifference(List<SocCheckItemDO> socCheckItemList, SocCheckDO socCheckDO, TokenUser tokenUser) {
        // 个人社保,个人公积金,公司社保,公司公积金赋初值
        BigDecimal personalSocDifference = new BigDecimal("0");
        BigDecimal personalPfDifference = new BigDecimal("0");
        BigDecimal companySocDifference = new BigDecimal("0");
        BigDecimal companyPfDifference = new BigDecimal("0");
        BigDecimal serviceCostDifference = new BigDecimal("0");
        BigDecimal socialDifference = new BigDecimal("0");
        BigDecimal accumulationDifference = new BigDecimal("0");
        BigDecimal negativeOne = new BigDecimal("-1");
        for (SocCheckItemDO socCheckItemDO : socCheckItemList) {
            // 个人社保计算差值
            BigDecimal sysPersonalSocAmount = socCheckItemDO.getSysPersonalSocAmount();
            BigDecimal excelPersonalSocAmount = socCheckItemDO.getExcelPersonalSocAmount();
            BigDecimal personalSocSubtract = sysPersonalSocAmount.subtract(excelPersonalSocAmount);
            if (personalSocSubtract.compareTo(BigDecimal.ZERO) < 0) {
                personalSocSubtract = personalSocSubtract.multiply(negativeOne);
            }
            personalSocDifference = personalSocDifference.add(personalSocSubtract);

            // 个人公积金计算差值
            BigDecimal sysPersonalPfAmount = socCheckItemDO.getSysPersonalPfAmount();
            BigDecimal excelPersonalPfAmount = socCheckItemDO.getExcelPersonalPfAmount();
            BigDecimal personalPfSubtract = sysPersonalPfAmount.subtract(excelPersonalPfAmount);
            if (personalPfSubtract.compareTo(BigDecimal.ZERO) < 0) {
                personalPfSubtract = personalPfSubtract.multiply(negativeOne);
            }
            personalPfDifference = personalPfDifference.add(personalPfSubtract);

            // 公司社保计算差值
            BigDecimal sysCompanySocAmount = socCheckItemDO.getSysCompanySocAmount();
            BigDecimal excelCompanySocAmount = socCheckItemDO.getExcelCompanySocAmount();
            BigDecimal companySocSubtract = sysCompanySocAmount.subtract(excelCompanySocAmount);
            if (companySocSubtract.compareTo(BigDecimal.ZERO) < 0) {
                companySocSubtract = companySocSubtract.multiply(negativeOne);
            }
            companySocDifference = companySocDifference.add(companySocSubtract);

            // 公司公积金计算差值
            BigDecimal sysCompanyPfAmount = socCheckItemDO.getSysCompanyPfAmount();
            BigDecimal excelCompanyPfAmount = socCheckItemDO.getExcelCompanyPfAmount();
            BigDecimal companyPfSubtract = sysCompanyPfAmount.subtract(excelCompanyPfAmount);
            if (companyPfSubtract.compareTo(BigDecimal.ZERO) < 0) {
                companyPfSubtract = companyPfSubtract.multiply(negativeOne);
            }
            companyPfDifference = companyPfDifference.add(companyPfSubtract);

            // 服务费计算差值
            BigDecimal sysServiceCost = socCheckItemDO.getSysServiceCost();
            BigDecimal excelServiceCost = socCheckItemDO.getExcelServiceCost();
            sysServiceCost = checkNull(sysServiceCost);
            excelServiceCost = checkNull(excelServiceCost);
            BigDecimal serviceCostSubtract = sysServiceCost.subtract(excelServiceCost);
            if (serviceCostSubtract.compareTo(BigDecimal.ZERO) < 0) {
                serviceCostSubtract = serviceCostSubtract.multiply(negativeOne);
            }
            serviceCostDifference = serviceCostDifference.add(serviceCostSubtract);

            // 补收滞纳金计算差值
            BigDecimal sysSocialSecurityFine = socCheckItemDO.getSysSocialSecurityFine();
            BigDecimal excelSocialSecurityFine = socCheckItemDO.getExcelSocialSecurityFine();
            sysSocialSecurityFine = checkNull(sysSocialSecurityFine);
            excelSocialSecurityFine = checkNull(excelSocialSecurityFine);
            BigDecimal socialSecurityFineSubtract = sysSocialSecurityFine.subtract(excelSocialSecurityFine);
            if (socialSecurityFineSubtract.compareTo(BigDecimal.ZERO) < 0) {
                socialSecurityFineSubtract = socialSecurityFineSubtract.multiply(negativeOne);
            }
            socialDifference = socialDifference.add(socialSecurityFineSubtract);

            // 补缴滞纳金计算差值
            BigDecimal sysAccumulationFundFine = socCheckItemDO.getSysAccumulationFundFine();
            BigDecimal excelAccumulationFundFine = socCheckItemDO.getExcelAccumulationFundFine();
            sysAccumulationFundFine = checkNull(sysAccumulationFundFine);
            excelAccumulationFundFine = checkNull(excelAccumulationFundFine);
            BigDecimal accumulationFundFineSubtract = sysAccumulationFundFine.subtract(excelAccumulationFundFine);
            if (accumulationFundFineSubtract.compareTo(BigDecimal.ZERO) < 0) {
                accumulationFundFineSubtract = accumulationFundFineSubtract.multiply(negativeOne);
            }
            accumulationDifference = accumulationDifference.add(accumulationFundFineSubtract);
        }
        // 赋差值
        socCheckDO.setSocDifferenceAmount(personalSocDifference.add(companySocDifference));
        socCheckDO.setPfDifferenceAmount(personalPfDifference.add(companyPfDifference));
        socCheckDO.setServiceCostDifference(serviceCostDifference);
        socCheckDO.setSocialDifference(socialDifference);
        socCheckDO.setAccumulationDifference(accumulationDifference);
        updateByIdSelective(socCheckDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 保存一个默认的校验对象
     */
    private SocCheckDO saveInitCheck(SocCheckAddDTO socCheckAddDTO, List<String> empSocInfoIdList, Boolean local, TokenUser tokenUser) {
        SocCheckVO save = save(socCheckAddDTO, tokenUser);
        SocCheckDO socCheckDO = selectDOById(save.getId(), tokenUser.getSiteId());
        // 查询需要解析的参保月中的所有参保人员
        List<EmpSocInfoDO> socInfoList = empSocInfoService.selectEmpInfoByMonthId(socCheckAddDTO.getCompanyId(),
                socCheckAddDTO.getSocMonthId(), tokenUser);
        List<String> collect;
        if (local) {
            collect = socInfoList.stream().map(EmpSocInfoDO::getId).collect(Collectors.toList());
        } else {
            collect = socInfoList.stream()
                    .filter(empSocInfoDO -> NORMAL_SOC.getValue().equals(empSocInfoDO.getEmpSocStatus()) ||
                            INCREMENT.getValue().equals(empSocInfoDO.getEmpSocStatus()))
                    .map(EmpSocInfoDO::getId)
                    .collect(Collectors.toList());
        }
        if (CollectionUtils.isEmpty(collect)) {
            throw new HrException("此月系统中无参保信息");
        }
        empSocInfoIdList.addAll(collect);
        return socCheckDO;
    }


    /**
     * 添加对象socCheckItemDO中insetList方法必须的属性
     */
    private void addSystemColumn(SocCheckItemDO socCheckItemDO, TokenUser tokenUser) {
        socCheckItemDO.setSiteId(tokenUser.getSiteId());
        socCheckItemDO.setGmtCreate(new Date());
        socCheckItemDO.setCreateUserId(tokenUser.getUserId());
        socCheckItemDO.setGmtModified(new Date());
        socCheckItemDO.setModifiedUserId(tokenUser.getUserId());
        socCheckItemDO.setValid(true);
    }

    /**
     * 官方数据校验入库
     */
    private void saveOfficialWrongsDate(SocCheckCustomQuery socCheckCustomQuery, List<String> empSocInfoIdList,
                                        List<SocCheckItemImportDTO> synthesisList, SocCheckDO socCheckDO, TokenUser tokenUser) {
        String year = socCheckCustomQuery.getYear();
        String month = socCheckCustomQuery.getMonth();
        String companyId = socCheckCustomQuery.getCompanyId();
        String monthId = socCheckCustomQuery.getMonthId();
        List<SocCheckItemDO> socCheckItemList = new ArrayList<>();
        for (SocCheckItemImportDTO socCheckItemImportDTO : synthesisList) {
            SocCheckItemDO socCheckItemDO = new SocCheckItemDO();
            socCheckItemDO.setSocCheckId(socCheckDO.getId());
            socCheckItemDO.setYear(year);
            socCheckItemDO.setMonth(month);
            socCheckItemDO.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getValue());
            socCheckItemDO.setCompanyId(companyId);
            socCheckItemDO.setEmpName(socCheckItemImportDTO.getName());
            addSystemColumn(socCheckItemDO, tokenUser);
            // 根据姓名身份证号查询系统中是否有此人
            EmpInfoDO empInfoDO = empInfoService.selectEmpByNameAndIdCard(socCheckItemImportDTO.getName(),
                    socCheckItemImportDTO.getIdCard(), companyId, tokenUser);
            // 系统内无网站中的这个人
            if (ObjectUtils.isEmpty(empInfoDO)) {
                // 封装一个子项
                encapsulationCheckItem(socCheckItemImportDTO, null, socCheckItemDO);
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SYS_NO_PERSON.getValue());
                socCheckItemList.add(socCheckItemDO);
                socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + 1);
                continue;
            }
            socCheckItemDO.setEmpId(empInfoDO.getId());
            socCheckItemDO.setIdCard(empInfoDO.getIdCard());
            EmpSocInfoDO empSocInfoDO = empSocInfoService.selectEmpSocInfoByYearMonthEmpIdPayCostTypeMonthId(year, month,
                    Objects.requireNonNull(SocPayCostType.findByDesc(socCheckItemImportDTO.getPayCostType())).getValue(),
                    empInfoDO.getId(), monthId, companyId, tokenUser.getSiteId());
            // 系统内有这个人但是网站中有数据但是系统中没数据
            if (ObjectUtils.isEmpty(empSocInfoDO)) {
                // 封装一个子项
                encapsulationCheckItem(socCheckItemImportDTO, null, socCheckItemDO);
                socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.NO_SOC.getValue());
                socCheckItemList.add(socCheckItemDO);
                socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + 1);
                continue;
            }
            encapsulationCheckItem(socCheckItemImportDTO, empSocInfoDO, socCheckItemDO);
            // 这边每匹配到一个  就删一个  最后剩下的就是  网站里面没有但是系统中有的
            empSocInfoIdList.remove(empSocInfoDO.getId());
            checkWrongs(socCheckCustomQuery.getCheckType(), socCheckItemDO, socCheckItemList, socCheckDO);
        }
        // 这里都是社保/医保/公积金网站没有的人
        if (!CollectionUtils.isEmpty(empSocInfoIdList)) {
            List<EmpSocInfoDO> excelNoExist = empSocInfoService.selectByIdList(empSocInfoIdList, tokenUser);
            List<String> empIdList = excelNoExist.stream().map(EmpSocInfoDO::getEmpId).collect(Collectors.toList());
            Map<String, EmpInfoDO> empInfoMap = empInfoService.selectDoByIds(empIdList)
                    .stream().collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
            for (EmpSocInfoDO empSocInfoDO : excelNoExist) {
                boolean haveWrongs = false;
                EmpInfoDO empInfoDO = empInfoMap.get(empSocInfoDO.getEmpId());
                SocCheckItemDO socCheckItemDO = new SocCheckItemDO();
                BeanUtils.copyProperties(empSocInfoDO, socCheckItemDO);
                socCheckItemDO.setId(null);
                socCheckItemDO.setSocCheckId(socCheckDO.getId());
                if (!ObjectUtils.isEmpty(empInfoDO)) {
                    socCheckItemDO.setIdCard(empInfoDO.getIdCard());
                    socCheckItemDO.setEmpName(empInfoDO.getName());
                }
                addSystemColumn(socCheckItemDO, tokenUser);
                encapsulationCheckItem(null, empSocInfoDO, socCheckItemDO);
                BigDecimal companySocAmount = new BigDecimal("0");
                BigDecimal personalSocAmount = new BigDecimal("0");
                BigDecimal companyMeAmount = new BigDecimal("0");
                BigDecimal personalMeAmount = new BigDecimal("0");
                List<EmpSocItemDO> empSocItemList =
                        empSocItemService.selectByEmpSocId(empSocInfoDO.getId(), tokenUser.getSiteId());
                List<EmpSocItemDO> socItemList = empSocItemList.stream()
                        .filter(empSocItemDO -> SOC_ITEM_TYPE.equals(empSocItemDO.getItemType()))
                        .collect(Collectors.toList());
                List<EmpSocItemDO> meItemList = empSocItemList.stream()
                        .filter(empSocItemDO -> ME_ITEM_TYPE.equals(empSocItemDO.getItemType()))
                        .collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(socItemList)) {
                    for (EmpSocItemDO empSocItemDO : socItemList) {
                        companySocAmount = companySocAmount.add(empSocItemDO.getAmountCompany());
                        personalSocAmount = personalSocAmount.add(empSocItemDO.getAmountPersonal());
                    }
                }
                if (!CollectionUtils.isEmpty(meItemList)) {
                    for (EmpSocItemDO empSocItemDO : meItemList) {
                        companyMeAmount = companyMeAmount.add(empSocItemDO.getAmountCompany());
                        personalMeAmount = personalMeAmount.add(empSocItemDO.getAmountPersonal());
                    }
                }
                if (empSocInfoDO.getCompanyPfAmount().compareTo(BigDecimal.ZERO) != 0 ||
                        empSocInfoDO.getPersonalPfAmount().compareTo(BigDecimal.ZERO) != 0) {
                    if (socCheckCustomQuery.getPfIsTrue()) {
                        socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.PF_SITE_NO_PERSON.getValue());
                        haveWrongs = true;
                    }
                }
                if (companySocAmount.compareTo(BigDecimal.ZERO) != 0 ||
                        personalSocAmount.compareTo(BigDecimal.ZERO) != 0) {
                    if (socCheckCustomQuery.getSocIsTrue()) {
                        socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.SOC_SITE_NO_PERSON.getValue());
                        haveWrongs = true;
                    }
                }
                if (companyMeAmount.compareTo(BigDecimal.ZERO) != 0 ||
                        personalMeAmount.compareTo(BigDecimal.ZERO) != 0) {
                    if (socCheckCustomQuery.getSocIsTrue()) {
                        socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.ME_SITE_NO_PERSON.getValue());
                        haveWrongs = true;
                    }
                }
                if (ObjectUtils.isEmpty(empInfoDO)) {
                    socCheckItemDO.setWrongsStatus(SocCheckWrongsStatus.EMP_NOT_EXIST.getValue());
                    haveWrongs = true;
                }
                if (haveWrongs) {
                    socCheckItemList.add(socCheckItemDO);
                }
            }
            socCheckDO.setEmpDifferenceNum(socCheckDO.getEmpDifferenceNum() + excelNoExist.size());
        }
        if (!CollectionUtils.isEmpty(socCheckItemList)) {
            calculationDifference(socCheckItemList, socCheckDO, tokenUser);
            socCheckItemDAO.insertListAndSetId(socCheckItemList);
        }

    }

    /**
     * 分别判断社保医保公积金是否校验,拿到对应社保局医保局公积金网站的数据
     */
    private void checkWebSite(SocCheckCustomQuery socCheckCustomQuery, Map<String, List<SyncSocUniversalDTO>> resultMap,
                              List<SyncSocUniversalDTO> socList, List<SyncSocUniversalDTO> pfList, List<SyncSocUniversalDTO> meList) {
        if (socCheckCustomQuery.getSocIsTrue()) {
            List<SyncSocUniversalDTO> sysSocCheckList = resultMap.get(SOC);
            if (!CollectionUtils.isEmpty(sysSocCheckList)) {
                socList.addAll(sysSocCheckList);
            }
        }
        if (socCheckCustomQuery.getMeIsTrue()) {
            List<SyncSocUniversalDTO> sysMeCheckList = resultMap.get(ME);
            if (!CollectionUtils.isEmpty(sysMeCheckList)) {
                meList.addAll(sysMeCheckList);
            }
        }
        if (socCheckCustomQuery.getPfIsTrue()) {
            List<SyncSocUniversalDTO> sysPfCheckList = resultMap.get(PF);
            if (!CollectionUtils.isEmpty(sysPfCheckList)) {
                pfList.addAll(sysPfCheckList);
            }
        }
    }

    /**
     * 把官方数据封装一下,以员工的IdCard分组计算每个员工的金额
     */
    private void packageOfficialDate(List<SyncSocUniversalDTO> officialCheckList,
                                     List<OfficialPackageDTO> packageList) {
        Map<String, List<SyncSocUniversalDTO>> groupList = officialCheckList.stream()
                .collect(Collectors.groupingBy(SyncSocUniversalDTO::getIdCard));
        for (String socIdCard : groupList.keySet()) {
            OfficialPackageDTO socPackage = new OfficialPackageDTO();
            List<SyncSocUniversalDTO> socGroupList = groupList.get(socIdCard);
            socPackage.setIdCard(socIdCard);
            socPackage.setEmpName(socGroupList.get(0).getEmpName());
            socPackage.setPersonalAmount(new BigDecimal("0"));
            socPackage.setCompanyAmount(new BigDecimal("0"));
            for (SyncSocUniversalDTO syncSocUniversalDTO : socGroupList) {
                socPackage.setPersonalAmount(socPackage.getPersonalAmount()
                        .add(new BigDecimal(syncSocUniversalDTO.getAmountPersonal())));
                socPackage.setCompanyAmount(socPackage.getCompanyAmount()
                        .add(new BigDecimal(syncSocUniversalDTO.getAmountCompany())));
            }
            packageList.add(socPackage);
        }
    }

    /**
     * 把社保局医保局的数据合成
     */
    private List<SocCheckItemImportDTO> synthesisSocPf(String year, String month, List<OfficialPackageDTO> socPackageList,
                                                       List<OfficialPackageDTO> pfPackageList) {
        // 这里复用一下本地校验的Excel实体类,因为属性差不多都一样,懒得再创建一个类了
        List<SocCheckItemImportDTO> result = new ArrayList<>();
        // 把公积金的集合变成 <idCard,OfficialPackageDTO> 形式
        Map<String, OfficialPackageDTO> groupPf = pfPackageList.stream()
                .collect(Collectors.toMap(OfficialPackageDTO::getIdCard, v -> v));
        // 把公积金集合里面的所有IdCard拿到
        List<String> pfIdCardList = pfPackageList.stream()
                .map(OfficialPackageDTO::getIdCard)
                .collect(Collectors.toList());
        for (OfficialPackageDTO officialPackageDTO : socPackageList) {
            SocCheckItemImportDTO socCheckItemImportDTO = new SocCheckItemImportDTO();
            socCheckItemImportDTO.setName(officialPackageDTO.getEmpName());
            socCheckItemImportDTO.setIdCard(officialPackageDTO.getIdCard());
            socCheckItemImportDTO.setBelongsDate(year + month);
            socCheckItemImportDTO.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getDesc());
            OfficialPackageDTO existPf = groupPf.get(officialPackageDTO.getIdCard());
            // 如果身份证号能匹配上则把两个对象合成一个对象
            if (!ObjectUtils.isEmpty(existPf)) {
                //匹配上一个就从IdCard集合中删除一个
                pfIdCardList.remove(officialPackageDTO.getIdCard());
                socCheckItemImportDTO.setPersonalPfAmount(existPf.getPersonalAmount().toPlainString());
                socCheckItemImportDTO.setCompanyPfAmount(existPf.getCompanyAmount().toPlainString());
            } else {
                //匹配不上则创建一个公积金为0的对象
                socCheckItemImportDTO.setPersonalPfAmount("0");
                socCheckItemImportDTO.setCompanyPfAmount("0");
            }
            socCheckItemImportDTO.setServiceCost("0");
            socCheckItemImportDTO.setAccumulationFundFine("0");
            socCheckItemImportDTO.setSocialSecurityFine("0");
            socCheckItemImportDTO.setPersonalSocAmount(officialPackageDTO.getPersonalAmount().toPlainString());
            socCheckItemImportDTO.setCompanySocAmount(officialPackageDTO.getCompanyAmount().toPlainString());
            result.add(socCheckItemImportDTO);
        }
        // 上面循环完毕这里如果pfIdCardList还有剩余说明是只有公积金没有社保
        for (String pfIdCard : pfIdCardList) {
            OfficialPackageDTO pfPackageDTO = groupPf.get(pfIdCard);
            // 这里就是创建一个社保为0的对象
            SocCheckItemImportDTO socCheckItemImportDTO = new SocCheckItemImportDTO();
            socCheckItemImportDTO.setName(pfPackageDTO.getEmpName());
            socCheckItemImportDTO.setIdCard(pfPackageDTO.getIdCard());
            socCheckItemImportDTO.setBelongsDate(year + month);
            socCheckItemImportDTO.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getDesc());
            socCheckItemImportDTO.setPersonalSocAmount("0");
            socCheckItemImportDTO.setCompanySocAmount("0");
            socCheckItemImportDTO.setPersonalPfAmount(pfPackageDTO.getPersonalAmount().toPlainString());
            socCheckItemImportDTO.setCompanyPfAmount(pfPackageDTO.getCompanyAmount().toPlainString());
            socCheckItemImportDTO.setServiceCost("0");
            socCheckItemImportDTO.setAccumulationFundFine("0");
            socCheckItemImportDTO.setSocialSecurityFine("0");
            result.add(socCheckItemImportDTO);
        }
        return result;
    }

    /**
     * 根据查询参数，构建example
     *
     * @param socCheckQuery 查询参数
     * @param siteId        所属站点id
     * @return example
     */
    private Example buildExample(SocCheckQuery socCheckQuery, String siteId) {
        Example example = new Example(SocCheckDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        // 排序
        ExampleKit.setExampleOrder(example, socCheckQuery.getOrders());
        return example;
    }
}




